package cn.microservices.cloud.gateway.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.server.*;
import org.springframework.web.reactive.result.view.ViewResolver;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 全局异常处理
 */
@Configuration
public class GlobalExceptionConfig {
	private Logger log = LoggerFactory.getLogger(getClass());

	private ServerProperties serverProperties;

	private ApplicationContext applicationContext;

	private ResourceProperties resourceProperties;

	private List<ViewResolver> viewResolvers;

	private ServerCodecConfigurer serverCodecConfigurer;



	public GlobalExceptionConfig(ServerProperties serverProperties,
                                 ResourceProperties resourceProperties,
                                 ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                 ServerCodecConfigurer serverCodecConfigurer,
                                 ApplicationContext applicationContext) {
		this.serverProperties = serverProperties;
		this.applicationContext = applicationContext;
		this.resourceProperties = resourceProperties;
		this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
		this.serverCodecConfigurer = serverCodecConfigurer;
	}

	@Primary
	@Bean
	@Order(Ordered.HIGHEST_PRECEDENCE)
	public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
		GlobalExceptionHandler exceptionHandler = new GlobalExceptionHandler(
			errorAttributes,
			this.resourceProperties,
			this.serverProperties.getError(),
			this.applicationContext);
		exceptionHandler.setViewResolvers(this.viewResolvers);
		exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
		exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
		return exceptionHandler;
	}

	public class GlobalExceptionHandler extends DefaultErrorWebExceptionHandler {


		public GlobalExceptionHandler(ErrorAttributes errorAttributes,
                                      ResourceProperties resourceProperties,
                                      ErrorProperties errorProperties,
                                      ApplicationContext applicationContext) {

			super(errorAttributes, resourceProperties, errorProperties, applicationContext);
		}


		@Override
		protected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
			log.error("api网关发生全局错误", super.getError(request));
			Map<String, Object> errorAttributes = new HashMap<>(4);
			errorAttributes.put("code", HttpStatus.NOT_FOUND.value());
			errorAttributes.put("msg", super.getError(request).getMessage());
			errorAttributes.put("data", "");
			errorAttributes.put("count", "");
			log.error("网关错误信息={}",super.getError(request).getMessage());
			return errorAttributes;
		}


		@Override
		protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
			return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
		}

		@Override
		protected int getHttpStatus(Map<String, Object> errorAttributes) {
			return HttpStatus.OK.value();
		}
	}
}

